package oop.ex3.filemanager;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.List;

import oop.ex3.common.CommunicationSession;
import oop.ex3.common.Protocol;
import oop.ex3.common.SocketCommunicationSession;

public class NameServer {

	// date members:
	private int _localPort;
	private String _host;
	private int _port;

	public NameServer(int localPort, String host, int port) {
		_localPort = localPort;
		_port = port;
		_host = host;
	}

	public String getHost() {
		return _host;
	}

	public int getPort() {
		return _port;
	}

	public List<RemoteFileManager> retrieveFileManagersWithFile(String wantedFile) {
		List<RemoteFileManager> fileMenagersWithWantedFile = new ArrayList<RemoteFileManager>();
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			newSession.sendString(Protocol.MSG_WANT_FILE);
			newSession.sendString(wantedFile);
			newSession.sendString(Protocol.MSG_END);
			
			//TODO what if the server doesn't reply with DONE END?
			//reading DONE:
			newSession.readString();
			//reading END:
			newSession.readString();
			String reply = newSession.readString();

			if(reply.equals(Protocol.MSG_FILE_NOT_FOUND)){
				Protocol.endSession(newSession);
				return fileMenagersWithWantedFile;
			}
			while (!reply.equals(Protocol.MSG_END_LIST)) {
				if (reply.equals(Protocol.MSG_FILE_ADDRESS)) {
					// every line that starts with "FILEADDRESS" represents a FM with the desired file,
					// this FM is added to the fileManagersWithWantedFile list:
					String fileManagerHost = newSession.readString();
					int fileManagerPort = newSession.readInt();
					// Read END
					newSession.readString();
					fileMenagersWithWantedFile.add(new RemoteFileManager(fileManagerHost, fileManagerPort));

					// Read the first token of the next line
					reply = newSession.readString();
				} else {
					// TODO Error
				}
			}
			// Read the END string
			newSession.readString();
			Protocol.endSession(newSession);
			}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			}
		return fileMenagersWithWantedFile;	
		}


	
	private CommunicationSession createCommunicationSession() throws IOException {
		return new SocketCommunicationSession(_host, _port);
		//		return new ConsoleCommunicationSession();
	}

	
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof NameServer)) {
			return false;
		}
		NameServer other = (NameServer) obj;
		return this._host.equals(other._host) && this._port == other._port;
	}


	public List<NameServer> requestMoreServers() {
		List<NameServer> allServers = new ArrayList<NameServer>();
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			newSession.sendString(Protocol.MSG_WANT_SERVERS);
			newSession.sendString(Protocol.MSG_END);
			String message = newSession.readString();
			String newServerHost;
			int newServerPort;
			NameServer newNameServer;
			while(!message.equals(Protocol.MSG_END_LIST)){
				newServerHost = newSession.readString();
				newServerPort = newSession.readInt();
				//reading End:
				newSession.readString();
				newNameServer = new NameServer(_localPort, newServerHost, newServerPort);
				allServers.add(newNameServer);
				message = newSession.readString();
			}
			//reading end:
			newSession.readString();
			Protocol.endSession(newSession);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return allServers;
	}

	
	public List<String> getAllFiles() {
		List<String> allFiles = new ArrayList<String>();
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
			newSession.sendString(Protocol.MSG_WANT_ALL_FILES);
			newSession.sendString(Protocol.MSG_END);
			String message = newSession.readString();
			
			
			if(message.equals(Protocol.MSG_NO_KNOWN_FILES)){
				return allFiles;
			}else{
				String fileName;
				while(!message.equals(Protocol.MSG_END_LIST)){
					fileName = newSession.readString();
					allFiles.add(fileName);
					//reading end:
					newSession.readString();
					message = newSession.readString();
				}
				//reading end:
				newSession.readString();
				Protocol.endSession(newSession);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return allFiles;
	}

	
	
	public void presentFileManager(List<File> dataBase, List<NameServer> allNameServers) {
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			
			String reply = newSession.readString();
			// Read the END string
			newSession.readString();
			// if the server's reply is "WELCOME", the FM sends a list of it's files and known servers:
			if (reply.equals(Protocol.MSG_WELCOME)) {
				// informing each server of this database:
				presentDataBase(newSession, dataBase);
				// informing each server of this name server list:
				presentListOfNameServers(newSession, allNameServers);
			}
			Protocol.endSession(newSession);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void presentDataBase(CommunicationSession newSession, List<File> dataBase) {
		String reply;
		try {
			for (File file : dataBase) {
				newSession.sendString(Protocol.MSG_CONTAIN_FILE);
				newSession.sendString(file.getName());
				newSession.sendString(Protocol.MSG_END);

				
				reply = newSession.readString();
				//Read END
				newSession.readString();
				
				if (!reply.equals(Protocol.MSG_DONE)) {
					// TODO add logic here, what do i do if the server doesn't reply?
				}
			}
			newSession.sendString(Protocol.MSG_END_LIST);
			newSession.sendString(Protocol.MSG_END);
			
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void presentListOfNameServers(CommunicationSession newSession, List<NameServer> allNameServers) {
		try {
			String reply;
			for (NameServer server : allNameServers) {
				newSession.sendString(Protocol.MSG_CONTAIN_NS);
				newSession.sendString(server.getHost());
				newSession.sendInt(server.getPort());
				newSession.sendString(Protocol.MSG_END);

				reply = newSession.readString();

				// Read END
				newSession.readString();
				if (!reply.equals(Protocol.MSG_DONE)) {
					// TODO add logic here, what do i do if the server doesn't reply?
				}
			}
			newSession.sendString(Protocol.MSG_END_LIST);
			newSession.sendString(Protocol.MSG_END);
			
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void notifyAddedFile(String newFile) {
			try {
				CommunicationSession newSession = createCommunicationSession();
				Protocol.startSession(newSession, _localPort);
				
				//reading done:
				newSession.readString();
				//reading end:
				newSession.readString();
				
				newSession.sendString(Protocol.MSG_CONTAIN_FILE);
				newSession.sendString(newFile);
				newSession.sendString(Protocol.MSG_END);
				
				//reading done:
				newSession.readString();
				//reading end:
				newSession.readString();
				
				Protocol.endSession(newSession);
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		

	}

	public void notifyRemovedFile(String removedFile) {
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
			newSession.sendString(Protocol.MSG_DONT_CONTAIN_FILE);
			newSession.sendString(removedFile);
			newSession.sendString(Protocol.MSG_END);
			
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
			Protocol.endSession(newSession);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	public void goodbey(){
		try {
			CommunicationSession newSession = createCommunicationSession();
			Protocol.startSession(newSession, _localPort);
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
			
			newSession.sendString(Protocol.MSG_GO_AWAY);
			newSession.sendString(Protocol.MSG_END);
			//reading done:
			newSession.readString();
			//reading end:
			newSession.readString();
			
			Protocol.endSession(newSession);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}
